﻿Imports System.Windows.Forms
Imports System.Drawing
Namespace SevenSegmentDisplay
    Public Class SevenSegmentDisplay
        Inherits Control
        Private M_digitSize As Integer = 15
        Private M_digitcount As Integer = 2
        Private M_digitColor As Color = Color.GreenYellow
        Private M_digitBorder As Integer = 2
        Private M_Value As Integer = 0
        Private m_DisplayOn As Boolean = True

        Public Property DigitSize() As Integer
            Get
                Return M_digitSize
            End Get
            Set(ByVal value As Integer)
                M_digitSize = value
                Me.Refresh()
            End Set
        End Property
        Public Property DigitCount() As Integer
            Get
                Return M_digitcount
            End Get
            Set(ByVal value As Integer)
                M_digitcount = value
                'setDimensions()
                Me.Refresh()
            End Set
        End Property

        Public Property DigitColor() As Color
            Get
                Return M_digitColor
            End Get
            Set(ByVal value As Color)
                M_digitColor = value
                Me.Refresh()
            End Set
        End Property
        Public Property DigitBorder() As Integer
            Get
                Return M_digitBorder
            End Get
            Set(ByVal value As Integer)
                M_digitBorder = value
                Me.Refresh()
            End Set
        End Property
        Public Property Value() As Integer
            Get
                Return M_Value
            End Get
            Set(ByVal value As Integer)
                M_Value = value
                Me.Refresh()
            End Set
        End Property
        Public Property DisplayOn() As Boolean
            Get
                Return m_DisplayOn
            End Get
            Set(ByVal value As Boolean)
                m_DisplayOn = value
                Me.Refresh()
            End Set
        End Property
        Public Sub New()
            Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
            Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
            Me.UpdateStyles()

        End Sub
        Private Sub _Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint

            Dim G As Graphics = e.Graphics
            G.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias
            G.Clear(Me.BackColor)

            Dim sevenSegmentHelper As New SevenSegmentHelper(G)
            Dim digitcount As String = "8"
            digitcount = digitcount.PadLeft(M_digitcount, "8")
            Dim digitSizeF As SizeF = sevenSegmentHelper.GetStringSize(digitcount, Font)
            Debug.Print("SEG: " & Font.ToString)

            Dim scaleFactor As Single = Math.Min(ClientSize.Width / digitSizeF.Width, M_digitSize / digitSizeF.Height)
            Dim font__1 As New Font(Font.FontFamily, scaleFactor * Font.SizeInPoints)
            digitSizeF = sevenSegmentHelper.GetStringSize(digitcount, font__1)
            G.FillRectangle(New SolidBrush(Me.BackColor), ((ClientSize.Width - digitSizeF.Width) / 2) - M_digitBorder, ((ClientSize.Height - digitSizeF.Height) / 2) - M_digitBorder, digitSizeF.Width + (M_digitBorder * 2), digitSizeF.Height + (M_digitBorder * 2))
            If m_DisplayOn Then
                Using brush As New SolidBrush(M_digitColor)
                    Using lightBrush As New SolidBrush(Color.FromArgb(20, M_digitColor))
                        sevenSegmentHelper.DrawDigits(M_Value.ToString, font__1, brush, lightBrush, (ClientSize.Width - digitSizeF.Width) / 2, (ClientSize.Height - digitSizeF.Height) / 2, M_digitcount)
                    End Using
                End Using
            End If

        End Sub

        Private Sub InitializeComponent()
            Me.SuspendLayout()
            '
            'SevenSegmentDisplay
            '
            Me.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
            Me.ResumeLayout(False)

        End Sub
    End Class

#Region "Helper Class"
    Public Class SevenSegmentHelper
        Private _graphics As Graphics

        ' Indicates what segments are illuminated for all 10 digits
        Shared _segmentData As Byte(,) = {{1, 1, 1, 0, 1, 1, 1}, _
        {0, 0, 1, 0, 0, 1, 0}, {1, 0, 1, 1, 1, 0, 1}, {1, 0, 1, 1, 0, 1, 1}, {0, 1, 1, 1, 0, 1, 0}, _
        {1, 1, 0, 1, 0, 1, 1}, {1, 1, 0, 1, 1, 1, 1}, {1, 0, 1, 0, 0, 1, 0}, {1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 0, 1, 1}, {0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}}

        ' Points that define each of the seven segments
        ReadOnly _segmentPoints As Point()() = New Point(6)() {}

        Public Sub New(ByVal graphics As Graphics)
            Me._graphics = graphics
            _segmentPoints(0) = New Point() {New Point(3, 2), New Point(39, 2), New Point(31, 10), New Point(11, 10)}
            _segmentPoints(1) = New Point() {New Point(2, 3), New Point(10, 11), New Point(10, 31), New Point(2, 35)}
            _segmentPoints(2) = New Point() {New Point(40, 3), New Point(40, 35), New Point(32, 31), New Point(32, 11)}
            _segmentPoints(3) = New Point() {New Point(3, 36), New Point(11, 32), New Point(31, 32), New Point(39, 36), New Point(31, 40), New Point(11, 40)}
            _segmentPoints(4) = New Point() {New Point(2, 37), New Point(10, 41), New Point(10, 61), New Point(2, 69)}
            _segmentPoints(5) = New Point() {New Point(40, 37), New Point(40, 69), New Point(32, 61), New Point(32, 41)}
            _segmentPoints(6) = New Point() {New Point(11, 62), New Point(31, 62), New Point(39, 70), New Point(3, 70)}
        End Sub

        Public Function GetStringSize(ByVal text As String, ByVal font As Font) As SizeF
            Dim sizef As New SizeF(0, _graphics.DpiX * font.SizeInPoints / 72)

            For i As Integer = 0 To text.Length - 1
                If [Char].IsDigit(text(i)) Then
                    sizef.Width += 42 * _graphics.DpiX * font.SizeInPoints / 72 / 72
                ElseIf text(i) = ":"c OrElse text(i) = "."c Then
                    sizef.Width += 12 * _graphics.DpiX * font.SizeInPoints / 72 / 72
                End If
            Next
            Return sizef
        End Function

        Public Sub DrawDigits(ByVal text As String, ByVal font As Font, ByVal brush As Brush, ByVal brushLight As Brush, ByVal x As Single, ByVal y As Single, ByVal l As Integer)
            text = text.PadLeft(l, "*")
            For cnt As Integer = 0 To text.Length - 1
                ' For digits 0-9
                If [Char].IsDigit(text(cnt)) Then
                    x = DrawDigit(Asc(text(cnt)) - Asc("0"), font, brush, brushLight, x, y)
                    ' For colon :
                ElseIf text(cnt) = ":"c Then
                    x = DrawColon(font, brush, x, y)
                    ' For dot .
                ElseIf text(cnt) = "."c Then
                    x = DrawDot(font, brush, x, y)
                ElseIf text(cnt) = "-"c Then
                    x = DrawDigit(10, font, brush, brushLight, x, y)
                ElseIf text(cnt) = "*"c Then
                    x = DrawDigit(11, font, brush, brushLight, x, y)
                End If
            Next
        End Sub

        Private Function DrawDigit(ByVal num As Integer, ByVal font As Font, ByVal brush As Brush, ByVal brushLight As Brush, ByVal x As Single, ByVal y As Single) As Single
            For cnt As Integer = 0 To _segmentPoints.Length - 1
                If _segmentData(num, cnt) = 1 Then
                    FillPolygon(_segmentPoints(cnt), font, brush, x, y)
                Else
                    FillPolygon(_segmentPoints(cnt), font, brushLight, x, y)
                End If
            Next
            Return x + 42 * _graphics.DpiX * font.SizeInPoints / 72 / 72
        End Function

        Private Function DrawDot(ByVal font As Font, ByVal brush As Brush, ByVal x As Single, ByVal y As Single) As Single
            Dim dotPoints As Point()() = New Point(0)() {}

            dotPoints(0) = New Point() {New Point(2, 64), New Point(6, 61), New Point(10, 64), New Point(6, 69)}

            For cnt As Integer = 0 To dotPoints.Length - 1
                FillPolygon(dotPoints(cnt), font, brush, x, y)
            Next
            Return x + 12 * _graphics.DpiX * font.SizeInPoints / 72 / 72
        End Function

        Private Function DrawColon(ByVal font As Font, ByVal brush As Brush, ByVal x As Single, ByVal y As Single) As Single
            Dim colonPoints As Point()() = New Point(1)() {}

            colonPoints(0) = New Point() {New Point(2, 21), New Point(6, 17), New Point(10, 21), New Point(6, 25)}
            colonPoints(1) = New Point() {New Point(2, 51), New Point(6, 47), New Point(10, 51), New Point(6, 55)}

            For cnt As Integer = 0 To colonPoints.Length - 1
                FillPolygon(colonPoints(cnt), font, brush, x, y)
            Next
            Return x + 12 * _graphics.DpiX * font.SizeInPoints / 72 / 72
        End Function

        Private Sub FillPolygon(ByVal polygonPoints As Point(), ByVal font As Font, ByVal brush As Brush, ByVal x As Single, ByVal y As Single)
            Dim polygonPointsF As PointF() = New PointF(polygonPoints.Length - 1) {}

            For cnt As Integer = 0 To polygonPoints.Length - 1
                polygonPointsF(cnt).X = x + polygonPoints(cnt).X * _graphics.DpiX * font.SizeInPoints / 72 / 72
                polygonPointsF(cnt).Y = y + polygonPoints(cnt).Y * _graphics.DpiY * font.SizeInPoints / 72 / 72
            Next
            _graphics.FillPolygon(brush, polygonPointsF)
        End Sub
    End Class
#End Region

End Namespace
